
export function grayImg(canvas_id) {
    let mat = cv.imread(canvas_id);
    let gray_img = new cv.Mat();
    cv.cvtColor(mat, gray_img, cv.COLOR_BGR2GRAY);
    cv.imshow(canvas_id, gray_img);
    mat.delete();
    gray_img.delete();
}

export function printMat(mat) {

    for (var i = 0; i < mat.cols; i++) {
        var s = "";
        for (var j = 0; j < mat.rows; j++) {
            s += mat.ucharPtr(i, j)[0];
        }
        console.log(s);
    }
}

export function extractRealImage(imgId, nx, ny, nw, nh){
    let allImg = cv.imread(imgId);
    let realRegion = new cv.Rect(nx, ny, nw, nh);
    let img = new cv.Mat();
    img = allImg.roi(realRegion);
    if(img.channels()==4){
        let matNoA = new cv.Mat();
        cv.cvtColor(img, matNoA, cv.COLOR_RGBA2RGB);
        img = matNoA;
    }
    allImg.delete();
    return img;
}

export function obejctRegionDetect(img) {   
    const width = img.cols;
    const height = img.rows;
    // console.log(width, height, width*height);
    let grayImg = new cv.Mat();
    cv.cvtColor(img, grayImg, cv.COLOR_RGB2GRAY);
    let mediaImg = new cv.Mat();
    cv.medianBlur(grayImg, mediaImg, 3);
    let binaryImg = new cv.Mat();
    let th = cv.threshold(mediaImg, binaryImg, 127, 255, cv.THRESH_OTSU);
    // cv.imshow(img_id, binaryImg)
    let label = new cv.Mat();
    let stats = new cv.Mat();
    let centroids = new cv.Mat();
    let n = cv.connectedComponentsWithStats(binaryImg, label, stats, centroids, 8);
    // printMat(label);
    let maxArea = height * width;
    let mask = new cv.Mat(height, width, cv.CV_8UC1, new cv.Scalar(255, 255, 255));
    let lt = [0, 0];
    let rt = [width, 0];
    let ld = [0, height];
    let rd = [width, height];
    for (var k = 1; k < n; k++) {
        let stat = stats.intPtr(k);
        //  console.log("s:", stat[0], stat[1], stat[2], stat[3], stat[4], maxArea)
        if (stat[2] * stat[3] > 0.4 * maxArea) {
            // console.log("s:", stat[0], stat[1], stat[2], stat[3], k, mask.channels())
            for (var i = 0; i < label.rows; i++) {
                for (var j = 0; j < label.cols; j++) {
                    if (label.ucharPtr(i, j) == k) {
                        mask.data[i * width + j] = 0;
                    }
                }
            }
            let realPoints = calculateEdgeLine(mask);
            lt = realPoints[0];
            rt = realPoints[1];
            ld = realPoints[2];
            rd = realPoints[3];
            break;
        }
    }
    //cv.imshow(img_id, mask);
    grayImg.delete();
    mediaImg.delete();
    binaryImg.delete();
    label.delete();
    // console.log([lt, rt, rd, ld]) 
    return [lt, rt, rd, ld];
}


function detectPeakVertical(mat) {
    const h = mat.rows;
    const w = mat.cols;
    const halfW = Math.round(w*0.4);
    let leftTopV, rightTopV, leftDownV, rightDownV;
    let flag = false;
    for(var j=0;j<h;j++){
        for(var i=0;i<halfW;i++){
            if(mat.ucharPtr(j, i)==0){
                leftTopV = [i, j];
                flag = true;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    flag = false;
    for(var j=0;j<h;j++){
        for(var i=w-1;i>w-halfW;i--){
            if(mat.ucharPtr(j, i)==0){
                rightTopV = [i, j];
                flag = true;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    flag = false;
    for(var j=h-1;j>0;j--){
        for(var i=0;i<halfW;i++){
            if(mat.ucharPtr(j, i)==0){
                leftDownV = [i, j];
                flag = true;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    flag = false;
    for(var j=h-1;j>0;j--){
        for(var i=w-1;i>w-halfW;i--){
            if(mat.ucharPtr(j, i)==0){
                rightDownV = [i, j];
                flag = true;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    return [leftTopV, rightTopV, leftDownV, rightDownV]
}

function detectPeakHorizontal(mat) {
    const h = mat.rows;
    const w = mat.cols;
    const halfH= Math.round(h*0.4);
    let leftTopH, rightTopH, leftDownH, rightDownH;
    let flag = false;
    for(var i=0;i<w;i++){
        for(var j=0;j<halfH;j++){
            if(mat.ucharPtr(j, i)==0){
                leftTopH = [i, j];
                flag = true;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    flag = false;
    for(var i=w-1;i>0;i--){
        for(var j=0;j<halfH;j++){
            if(mat.ucharPtr(j, i)==0){
                rightTopH = [i, j];
                flag = true;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    flag = false;
    for(var i=0;i<w;i++){
        for(var j=h-1;j>h-halfH;j--){
            if(mat.ucharPtr(j, i)==0){
                leftDownH = [i, j];
                flag = true;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    flag = false;
    for(var i=w-1;i>0;i--){
        for(var j=h-1;j>h-halfH;j--){
            if(mat.ucharPtr(j, i)==0){
                rightDownH = [i, j];
                flag = true;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    return [leftTopH, rightTopH, leftDownH, rightDownH]

}


function calculateEdgeLine(mat) {
    const h = mat.rows;
    const w = mat.cols;
    let points1 = detectPeakVertical(mat)
    let leftTop1 = points1[0];
    let rightTop1 = points1[1];
    let leftDown1 = points1[2];
    let rightDown1 = points1[3];
    let points2 = detectPeakHorizontal(mat)
    let leftTop2 = points2[0];
    let rightTop2 = points2[1];
    let leftDown2 = points2[2];
    let rightDown2 = points2[3];
    // console.log("points1:", points1);
    // console.log("points2:", points2);
    let leftTopDistance1 = Math.pow((leftTop1[0] - 0), 2) + Math.pow((leftTop1[1] - 0), 2);
    let leftTopDistance2 = Math.pow((leftTop2[0] - 0), 2) + Math.pow((leftTop2[1] - 0), 2);
    let leftTop, rightRop, leftDown, rightDown;
    // 左上上
    if (leftTopDistance1 < leftTopDistance2) {
        leftTop = leftTop1;
    } else {
        leftTop = leftTop2;
    }
    // 右上
    let rightTopDistance1 = Math.pow((rightTop1[0] - w), 2) + Math.pow((rightTop1[1] - 0), 2);
    let rightTopDistance2 = Math.pow((rightTop2[0] - w), 2) + Math.pow((rightTop2[1] - 0), 2);
    if (rightTopDistance1 < rightTopDistance2) {
        rightRop = rightTop1;
    } else {
        rightRop = rightTop2;
    }
    // 左下
    let leftDownDistance1 = Math.pow((leftDown1[0] - 0), 2) + Math.pow((leftDown1[1] - h), 2);
    let leftDownDistance2 = Math.pow((leftDown2[0] - 0), 2) + Math.pow((leftDown2[1] - h), 2);
    if (leftDownDistance1 < leftDownDistance2) {
        leftDown = leftDown1;
    } else {
        leftDown = leftDown2;
    }
    // 右下
    let rightDownDistance1 = Math.pow((rightDown1[0] - w), 2) + Math.pow((rightDown1[1] - h), 2);
    let rightDownDistance2 = Math.pow((rightDown2[0] - w), 2) + Math.pow((rightDown2[1] - h), 2);
    if (rightDownDistance1 < rightDownDistance2) {
        rightDown = rightDown1;
    } else {
        rightDown = rightDown2;
    }
    // console.log("calculateEdgeLine:", leftTop, rightRop, leftDown, rightDown);
    return [leftTop, rightRop, leftDown, rightDown];

}

export function imageShow(image, points, nx, ny, imgId, drawLine=true) {
    let c = new cv.Scalar(255, 0, 0);
    if(drawLine){
        cv.line(image, new cv.Point(points[0][0], points[0][1]), new cv.Point(points[1][0], points[1][1]), c, 2);
        cv.line(image, new cv.Point(points[1][0], points[1][1]), new cv.Point(points[2][0], points[2][1]), c, 2);
        cv.line(image, new cv.Point(points[2][0], points[2][1]), new cv.Point(points[3][0], points[3][1]), c, 2);
        cv.line(image, new cv.Point(points[3][0], points[3][1]), new cv.Point(points[0][0], points[0][1]), c, 2);
    }
    let newImage = new cv.Mat();
    let constant = new cv.Scalar(232, 232, 232)
    cv.copyMakeBorder(image, newImage, ny, ny, nx, nx, cv.BORDER_CONSTANT, constant);
    cv.imshow(imgId, newImage);
    newImage.delete();
}

export function obejecRegionHighLight(mat, lowLightImage, points){
    let matPtr = new cv.MatVector();
    const h = mat.rows;
    const w = mat.cols;
    let pointMat = new cv.Mat(4, 2, cv.CV_32SC1);
    for(var i=0;i<points.length;i++){
        pointMat.data32S[2*i] = points[i][0];
        pointMat.data32S[2*i+1] = points[i][1];
        // matPtr.push(new cv.Point(points[i][0], points[i][1])); 
    }
    matPtr.push_back(pointMat);
    let mask = new cv.Mat(h, w, cv.CV_8UC1, new cv.Scalar(0, 0, 0));
    let newImage = new cv.Mat();
    lowLightImage.copyTo(newImage);
    cv.fillPoly(mask, matPtr, new cv.Scalar(255, 255, 255));
    mat.copyTo(newImage, mask);
    mask.delete();
    return newImage;
}

export function adjustLight(img, scale=0.5){
    const h = img.rows;
    const w = img.cols;
    let HSVImg = new cv.Mat();
     
    cv.cvtColor(img, HSVImg, cv.COLOR_RGB2HSV)
    let HSVchannel =  new cv.MatVector();
    cv.split(HSVImg, HSVchannel);
    let VImage = HSVchannel.get(2);
    for(var i=0;i<h;i++){
        for(var j=0; j<w;j++){
            VImage.data[i*w+j]  = Math.round(VImage.data[i*w+j]*scale);
        }
    }
    let newImage = new cv.Mat();
    let tempImage = new cv.Mat();
    cv.merge(HSVchannel, tempImage)
    cv.cvtColor(tempImage, newImage, cv.COLOR_HSV2BGR)
    HSVImg.delete(); 
    HSVchannel.delete();
    tempImage.delete();
    VImage.delete();
    return newImage;
}

export function correctImage(img, points, scale=1){
    let arr = [];
    let x1 = 10000;
    let y1 = 10000;
    let x2 = -1;
    let y2 = -1;
    for(var i=0;i<points.length;i++){
        arr.push(points[i][0]/scale);
        arr.push(points[i][1]/scale);
        x1 = Math.min(x1, points[i][0]/scale);
        y1 = Math.min(y1, points[i][1]/scale);
        x2 = Math.max(x2, points[i][0]/scale);
        y2= Math.max(y2, points[i][1]/scale);

    }
    let srcMat= cv.matFromArray(4, 2, cv.CV_32FC1, arr);
    let w = Math.round(x2-x1);
    let h = Math.round(y2-y1);
    let dstMat = cv.matFromArray(4, 2, cv.CV_32FC1, [0, 0, w, 0, w, h, 0, h]);
    let M = cv.getPerspectiveTransform(srcMat, dstMat);
    let newImage = new cv.Mat();
    cv.warpPerspective(img, newImage, M, new cv.Size(w, h));
    return newImage;
     
}
export function turnLeft(img){
    let newImage = new cv.Mat();
    cv.rotate(img, newImage, cv.ROTATE_90_COUNTERCLOCKWISE);
    return newImage;
}

export function turnRight(img){
    let newImage = new cv.Mat();
    cv.rotate(img, newImage, cv.ROTATE_90_CLOCKWISE);
    return newImage;
}
    
export function imageZoom(img, w, h, imgId){
    const iw = img.cols;
    const ih = img.rows;
    const screenScale = h/w;
    const imgScale = ih/iw;
    let nx, ny, nw, nh;
    if(imgScale > screenScale) {
        nh = h;
        nw = Math.round(h/imgScale);
        nx = Math.round((w-nw)/2);
        ny = 0;
    }else{
        nx = 0;
        nw = w;
        nh = Math.round(w*imgScale);
        ny = Math.round((h-nh)/2);
    }
    let newImage = new cv.Mat();
    cv.resize(img, newImage, new cv.Size(nw, nh), 0, 0, cv.INTER_AREA);
    return [newImage, nx, ny];
}

function usm(img){
    let blurImg = new cv.Mat();
    cv.GaussianBlur(img, blurImg, new cv.Size(3, 3), 0);
    let lapImg = new cv.Mat();
    cv.Laplacian(blurImg, lapImg, -1, 1, 1.0, 0, cv.BORDER_DEFAULT);
    let newImg = new cv.Mat();
    cv.addWeighted(blurImg, 1.0, lapImg, -1.0, 0, newImg);
    blurImg.delete();
    lapImg.delete();
    return newImg;
}

export function autoEnhance1(img){
    console.log("on call autoEnhance ");
    let grayImg = new cv.Mat();   
    let binaryImg = new cv.Mat();
    let newImg = new cv.Mat();
    cv.cvtColor(img, grayImg, cv.COLOR_BGR2GRAY);
    // bilateralFilter 耗时严重 待优化
    // console.time("bilateralFilter")
    // let temp1 = new cv.Mat();
    // cv.bilateralFilter(grayImg, temp1, 40, 30, 30);
    // console.timeEnd("bilateralFilter") 
    let temp2  = usm(grayImg);
    cv.adaptiveThreshold(temp2, binaryImg, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 11, 12);
    cv.cvtColor(binaryImg, newImg, cv.COLOR_GRAY2BGR);
    grayImg.delete();
    binaryImg.delete();
    temp2.delete();
    return newImg;
}

export function autoEnhance2(img){
    let grayImg = new cv.Mat();
    cv.cvtColor(img, grayImg, cv.COLOR_BGR2GRAY);
    let newImg = new cv.Mat();
    cv.adaptiveThreshold(grayImg, newImg, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 21, 12);
    cv.cvtColor(newImg, newImg, cv.COLOR_GRAY2BGR);
    grayImg.delete();
    return newImg;
     
}


export function autoEnhance3(img){
    let grayImg = new cv.Mat();
    cv.cvtColor(img, grayImg, cv.COLOR_BGR2GRAY);
    let iw = img.cols;
    let ih = img.rows;
    let r = Math.round(Math.min(ih, iw)/16)
    let temp = new cv.Mat(ih, iw, cv.CV_32FC1, new cv.Scalar(255, 255, 255));
    let temp3 = new cv.Mat();
    let temp4 = new cv.Mat();
    if(r%2==0){
        r += 1;
    }
    let blurImg = new cv.Mat();
    cv.GaussianBlur(grayImg, blurImg, new cv.Size(r, r), 0);
    let grayImgFloat = new cv.Mat(), blurImgFloat = new cv.Mat();
    grayImg.convertTo(grayImgFloat, cv.CV_32F);
    blurImg.convertTo(blurImgFloat, cv.CV_32F);
    let divideMat = new cv.Mat();
    cv.divide(grayImgFloat, blurImgFloat, divideMat, 1, cv.CV_32F);
    cv.multiply(divideMat, grayImgFloat, temp3, 1, cv.CV_32F);
    temp3.convertTo(temp4, cv.CV_8U);
    let temp2 = usm(temp4);
    let binaryImg = new cv.Mat();
    let th = cv.threshold(temp2, binaryImg, 127, 255, cv.THRESH_OTSU);
    let table = getColorTable(th);
    let newImg = cvLUT(temp2, table);
    cv.cvtColor(newImg, newImg, cv.COLOR_GRAY2RGB);
    grayImg.delete();
    blurImg.delete();
    grayImgFloat.delete();
    blurImgFloat.delete();
    divideMat.delete();
    temp.delete();
    temp2.delete();
    temp3.delete();
    temp4.delete();
    table.delete();
    return newImg;
}

function getColorTable(th, alpha=0.05){
    let table = new cv.Mat(1, 256, cv.CV_8UC1, new cv.Scalar(255, 255, 255));
    for(var i=0;i<256;i++){
        let t = Math.round(1 / (1 + Math.E ** (-alpha*(i - th))) * 255);
        if(t < 0){
            t = 0;
        }
        if (t > 255){
            t = 255;
        }
        table.data[i] = t;
    }
    return table;
}

function cvLUT(img, table){
    console.log("on call lut");
    let iw = img.cols;
    let ih = img.rows;
    let newImg = new cv.Mat(ih, iw, cv.CV_8UC1, new cv.Scalar(255, 255, 255));
    for(var i=0;i<ih;i++){
        for(var j=0;j<iw;j++){
            
            newImg.data[i*iw+j] = table.data[img.data[i*iw+j]]
        }
    }
    return newImg;
}